package com.qtrue.thread3;

/**
 * @Package: com.qtrue.thread3
 * @ClassName: ThreadCommunicationExample
 * @Author: xiaoBao
 * @CreateTime: 2022-05-09 22:24
 * @Description:线程通信的例子：使用两个线程打印1-100，线程A打印1，线程B打印2...交替打印，以此类推。
 *
 *  涉及到的三个方法：
 *      1. wait()：一旦执行此方法，当前线程进入阻塞状态，并释放同步监视器。
 *      2. notify()：一旦执行此方法，就会唤醒被wait()的一个线程，如果有多个线程被wait，就会唤醒线程优先级最高的那个。
 *      3. notifyAll()：一旦执行此方法，就会唤醒所有被wait的线程。
 *
 *  说明：
 *      1. wait()、notify()、notifyAll()这三个方法必须使用在同步代码块或同步方法中。
 *      2. 三个方法的调用者必须是同步代码块或者同步方法中的同步监视器（代码块中直接写方法名），否则会出现异常。
 *
 *  sleep()与wait()的区别：
 *      相同点：一旦执行方法，都可以使当前线程进入阻塞状态。
 *      不同点：
 *          1. sleep()是线程类Thread中的方法，wait()是Object中的方法。
 *          2. sleep()不会释放锁，当一个synchronized代码块中调用了sleep() ，线程虽然进入休眠，但是对象的机锁没有被释放；
 *              wait()会释放，而且会将当前线程加入到等待队列中。
 *          3. sleep()不依赖于线程同步器synchronized，但是wait需要依赖synchronized关键字。
 *          4. sleep()不需要被唤醒（休眠之后退出阻塞），但是wait需要被唤醒
 */
public class ThreadCommunicationExample {
    public static void main(String[] args) {
        // 创建线程的实现类
        Number number = new Number();

        // 将实现类对象作为参数传递到Thread类的构造器当中
        Thread thread01 = new Thread(number);
        Thread thread02 = new Thread(number);

        // 设置线程名称
        thread01.setName("线程一");
        thread02.setName("线程二");

        // 启动线程
        thread01.start();
        thread02.start();
    }
}

class Number implements Runnable{
    private int num = 1;

    @Override
    public void run() {

        while(true){
            synchronized (this){

                // 唤醒正处于阻塞状态的线程。    当前只创建了两个线程，所以这里使用notify()就可以
                notify();
                // 唤醒所有正处于阻塞状态的线程
//                notifyAll();

                if (num <= 100){
                    System.out.println(Thread.currentThread().getName() + "：" + num);

                    try {
                        Thread.sleep(10); // 线程休眠
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                    num++;

                    try {
                        // wait()：使当前线程进入阻塞状态
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                    break;
                }
            }
        }

    }
}